package com.meiyuetao.myt.sale.service;

import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import lab.s2jh.auth.security.AuthUserHolder;
import lab.s2jh.bpm.service.ActivitiService;
import lab.s2jh.core.dao.BaseDao;
import lab.s2jh.core.service.BaseService;
import lab.s2jh.core.service.Validation;
import lab.s2jh.ctx.MailService;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import com.google.common.collect.Lists;
import com.meiyuetao.myt.core.constant.StorageModeEnum;
import com.meiyuetao.myt.core.constant.VoucherStateEnum;
import com.meiyuetao.myt.core.constant.VoucherTypeEnum;
import com.meiyuetao.myt.core.service.VoucherNumGenerateService;
import com.meiyuetao.myt.customer.entity.CustomerProfile;
import com.meiyuetao.myt.finance.dao.BizTradeUnitDao;
import com.meiyuetao.myt.finance.entity.AccountInOut;
import com.meiyuetao.myt.finance.service.AccountInOutService;
import com.meiyuetao.myt.job.BusinessNotifyService;
import com.meiyuetao.myt.md.dao.CommodityDao;
import com.meiyuetao.myt.md.dao.CommodityVaryPriceDao;
import com.meiyuetao.myt.md.entity.Commodity;
import com.meiyuetao.myt.md.entity.CommodityVaryPrice;
import com.meiyuetao.myt.purchase.dao.PurchaseOrderDao;
import com.meiyuetao.myt.purchase.dao.SupplierDao;
import com.meiyuetao.myt.purchase.entity.PurchaseOrder;
import com.meiyuetao.myt.purchase.entity.PurchaseOrderDetail;
import com.meiyuetao.myt.purchase.entity.Supplier;
import com.meiyuetao.myt.sale.dao.BoxOrderDao;
import com.meiyuetao.myt.sale.dao.BoxOrderDetailCommodityDao;
import com.meiyuetao.myt.sale.dao.BoxOrderDetailDao;
import com.meiyuetao.myt.sale.dao.OrderTracingDao;
import com.meiyuetao.myt.sale.dao.SaleDeliveryDao;
import com.meiyuetao.myt.sale.dao.SaleDeliveryDetailDao;
import com.meiyuetao.myt.sale.dao.SaleReturnDao;
import com.meiyuetao.myt.sale.dao.SaleReturnDetailDao;
import com.meiyuetao.myt.sale.entity.BoxOrder;
import com.meiyuetao.myt.sale.entity.BoxOrder.BoxOrderStatusEnum;
import com.meiyuetao.myt.sale.entity.BoxOrderDetail;
import com.meiyuetao.myt.sale.entity.BoxOrderDetail.BoxOrderDetailStatusEnum;
import com.meiyuetao.myt.sale.entity.BoxOrderDetailCommodity;
import com.meiyuetao.myt.sale.entity.OrderTracing;
import com.meiyuetao.myt.sale.entity.SaleDelivery;
import com.meiyuetao.myt.sale.entity.SaleDelivery.DeliveryProcessTypeEnum;
import com.meiyuetao.myt.sale.entity.SaleDeliveryDetail;
import com.meiyuetao.myt.sale.entity.SaleReturn;
import com.meiyuetao.myt.sale.entity.SaleReturn.SaleReturnTypeEnum;
import com.meiyuetao.myt.sale.entity.SaleReturnDetail;
import com.meiyuetao.myt.sale.vo.SaleAndReturnDetailVo;
import com.meiyuetao.myt.stock.dao.StorageLocationDao;
import com.meiyuetao.myt.stock.entity.CommodityStock;
import com.meiyuetao.myt.stock.entity.StockInOut;
import com.meiyuetao.myt.stock.entity.StorageLocation;
import com.meiyuetao.myt.stock.service.CommodityStockService;
import com.meiyuetao.myt.stock.service.StockInOutService;

@Service
@Transactional
public class SaleDeliveryService extends BaseService<SaleDelivery, Long> {

    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private SaleDeliveryDao saleDeliveryDao;
    @Autowired
    private CommodityStockService commodityStockService;
    @Autowired
    private StorageLocationDao storageLocationDao;
    @Autowired
    private CommodityDao commodityDao;
    @Autowired
    private BoxOrderDao boxOrderDao;
    @Autowired
    private BoxOrderDetailDao boxOrderDetailDao;
    @Autowired
    private BoxOrderDetailCommodityDao boxOrderDetailCommodityDao;
    @Autowired
    private BoxOrderDao commodityVaDao;
    @Autowired
    private CommodityVaryPriceDao commodityVaryPriceDao;
    @Autowired
    private BizTradeUnitDao bizTradeUnitDao;

    @Autowired
    private AccountInOutService accountInOutService;
    @Autowired
    private VoucherNumGenerateService voucherNumGenerateService;
    @Autowired
    private SaleDeliveryDetailDao saleDeliveryDetailDao;
    @Autowired
    private StockInOutService stockInOutService;
    @Autowired
    private SupplierDao supplierDao;
    @Autowired
    private BusinessNotifyService businessNotifyService;
    @Autowired
    private SaleReturnDetailDao saleReturnDetailDao;
    @Autowired
    private SaleReturnDao saleReturnDao;
    @Autowired
    private PurchaseOrderDao purchaseOrderDao;

    @Autowired(required = false)
    private ActivitiService activitiService;
    @Autowired
    private MailService mailService;
    @Autowired
    private OrderTracingDao orderTracingDao;

    @Override
    protected BaseDao<SaleDelivery, Long> getEntityDao() {
        return saleDeliveryDao;
    }

    private CommodityStock findCommodityCommodityStock(SaleDeliveryDetail sdd) {

        Commodity commodity = commodityDao.findOne(sdd.getCommodity().getId());
        StorageLocation storageLocation = storageLocationDao.findOne(sdd.getStorageLocation().getId());
        String batchNo = sdd.getBatchNo();
        CommodityStock commodityStock = commodityStockService.findBy(commodity, storageLocation, batchNo);
        Validation.isTrue(commodityStock != null, "须先初始化维护库存数据: 商品=[" + commodity.getDisplay() + "],库存地=[" + storageLocation.getDisplay() + "],批次号=[" + batchNo + "]");

        boolean allowOversold = commodityStock.getCommodity().getAllowOversold();
        if (!allowOversold) {
            int saleQuantity = sdd.getQuantity().intValue();
            int curStockQuantity = commodityStock.getCurStockQuantity().intValue();
            // 不允许超卖，判断库存量
            Validation.isTrue((curStockQuantity - saleQuantity) >= 0, "商品库存量不足: 商品=[" + commodity.getDisplay() + "],库存地=[" + storageLocation.getDisplay() + "],批次号=[" + batchNo
                    + "]当前库存量=[" + curStockQuantity + "]");
        }

        Validation.isTrue(commodityStock.getCostPrice() != null && commodityStock.getCostPrice().doubleValue() > 0, "成本价格无效: 商品=[" + commodity.getDisplay() + "],库存地=["
                + storageLocation.getDisplay() + "],批次号=[" + batchNo + "],成本价=[" + commodityStock.getCostPrice() + "]");
        return commodityStock;
    }

    private void saveDetails(SaleDelivery entity) {
        // 如果不是“代发”的销售单，才进行航行成本金额的计算的处理
        if (!StorageModeEnum.SA.equals(entity.getStorageMode())) {
            BigDecimal commodityCostAmount = BigDecimal.ZERO;
            for (SaleDeliveryDetail sdd : entity.getSaleDeliveryDetails()) {
                sdd.setSaleDelivery(entity);

                CommodityStock commodityStock = findCommodityCommodityStock(sdd);
                sdd.setCostPrice(commodityStock.getCostPrice());
                sdd.setCostAmount(commodityStock.getCostPrice().multiply(sdd.getQuantity()));
                commodityCostAmount = commodityCostAmount.add(sdd.getCostAmount());

                if (entity.getSubmitDate() != null) {
                    BoxOrderDetailCommodity bodc = null;
                    if (sdd.getBoxOrderDetailCommodity() != null) {
                        bodc = boxOrderDetailCommodityDao.findOne(sdd.getBoxOrderDetailCommodity().getId());
                        // Validation.isTrue(sdd.getQuantity().equals(bodc.getQuantity()),
                        // "如果是关联订单行项商品，数量必须一致");
                    }
                    if (sdd.getSaleReturnDetail() != null) {
                        SaleReturnDetail srd = saleReturnDetailDao.findOne(sdd.getSaleReturnDetail().getId());
                        // Validation.isTrue(sdd.getQuantity().equals(srd.getQuantity()),
                        // "如果是关联退换货行项商品，数量必须一致");
                    }

                }
            }

            entity.setCommodityCostAmount(commodityCostAmount);
            entity.setTotalCostAmount(commodityCostAmount.add(entity.getLogisticsAmount() == null ? BigDecimal.ZERO : entity.getLogisticsAmount()));
        }
    }

    public void bpmCreate(SaleDelivery entity, Map<String, Object> variables) {
        saveDetails(entity);
        saleDeliveryDao.save(entity);
        activitiService.startProcessInstanceByKey("BPM_SALE_DELIVERY", entity);
    }

    public void bpmUpdate(SaleDelivery entity, String taskId, Map<String, Object> variables) {
        Validation.isTrue(entity.getRedwordDate() == null, "销售单已经红冲");
        saveDetails(entity);
        saleDeliveryDao.save(entity);
        activitiService.completeTask(taskId, variables);
    }

    /*
     * @Async public void sendMessage(String phone, String content) { int i = 0;
     * Boolean success = false; SmsLog smsLog = new SmsLog();
     * smsLog.setSendContent(content); smsLog.setSendToList(phone); SmsService
     * smsService = null; if
     * (SmsChannelEnum.STONG.equals(smsLog.getSmsChannel())) { smsService =
     * SpringContextHolder.getBean(ThreeTongSmsService.class); } else if
     * (SmsChannelEnum.QYQQ.equals(smsLog.getSmsChannel())) { smsService =
     * SpringContextHolder.getBean(QyqqSmsService.class); } while (i <
     * MAX_RETRY_TIMES && !success) { success = smsService.send(phone, content,
     * smsLog); i++; } if (!success) { smsLog.setFailureToList(phone); }
     * smsLogService.save(smsLog); }
     */

    public SaleDelivery pickSave(SaleDelivery entity) {
        entity = saleDeliveryDao.findOne(entity.getId());
        Validation.isTrue(entity.getRedwordDate() == null, "销售单已经红冲");
        Validation.isTrue(entity.getAuditDate() != null, "销售单通过审核后才能拣货");
        Validation.isTrue(entity.getPickTime() == null, "已经完成拣货，不能重复拣货");
        entity.setPickTime(new Date());
        entity.setProcessType(DeliveryProcessTypeEnum.PICK);
        entity.setLastOperationSummary(entity.buildLastOperationSummary("拣货"));
        entity.setPickUser(AuthUserHolder.getLogonUser());

        List<SaleDeliveryDetail> saleDeliveryDetails = entity.getSaleDeliveryDetails();
        Set<BoxOrderDetail> boxOrderDetails = new HashSet<BoxOrderDetail>();
        for (SaleDeliveryDetail sdd : saleDeliveryDetails) {
            BoxOrderDetailCommodity bodc = sdd.getBoxOrderDetailCommodity();
            if (bodc != null) {
                boxOrderDetails.add(bodc.getBoxOrderDetail());
                if (bodc.getDeliveriedQuantity() == null) {
                    bodc.setDeliveriedQuantity(sdd.getQuantity());
                } else {
                    bodc.setDeliveriedQuantity(sdd.getQuantity().add(bodc.getDeliveriedQuantity()));
                }
                boxOrderDetailCommodityDao.save(bodc);
            }
            // 如果不是“代发”的销售单，才进行库存量的处理
            if (!StorageModeEnum.SA.equals(entity.getStorageMode())) {
                StockInOut stockInOut = buildDefaultStockInOut(entity, sdd);
                CommodityStock commodityStock = findCommodityCommodityStock(sdd);
                stockInOut.setDiffSalingQuantity(sdd.getQuantity().negate());
                stockInOut.setDiffQuantity(sdd.getQuantity().negate());
                stockInOut.setDiffStockAmount(commodityStock.getCostPrice().multiply(sdd.getQuantity().negate()));
                stockInOut.setOperationSummary("拣货出库将锁定库存量转为实际库存量扣减");
                stockInOutService.saveCascade(stockInOut);
            }

        }

        return saleDeliveryDao.save(entity);
    }

    public void deliverySave(SaleDelivery entity) {
        Validation.isTrue(entity.getRedwordDate() == null, "销售单已经红冲");
        // 代发订单并且没拣货，发货时默认拣货
        // if (DeliveryTypeEnum.AGENT.equals(entity.getDeliveryType()) &&
        // entity.getPickTime() == null) {
        if (entity.getPickTime() == null) {
            entity = pickSave(entity);
        }
        entity.setProcessType(DeliveryProcessTypeEnum.DELIVERY);
        if (entity.getDeliveryTime() == null) {
            entity.setDeliveryTime(new Date());
        }
        entity.setLastOperationSummary(entity.buildLastOperationSummary("发货"));
        entity.setDeliveryUser(AuthUserHolder.getLogonUser());

        // 销售实现记账
        List<AccountInOut> accountInOuts = Lists.newArrayList();
        // 运费记账
        BigDecimal logisticsAmount = entity.getLogisticsAmount();
        if (logisticsAmount != null && logisticsAmount.floatValue() != 0) {
            Validation.notNull(entity.getLogistics(), "有运费金额必须有对应快递公司");

            // 贷：2202=应付账款
            AccountInOut accountInOut1 = buildDefaultAccountInOut(entity);
            Supplier logistics = entity.getLogistics();
            accountInOut1.setBizTradeUnit(bizTradeUnitDao.findOne(logistics.getBizTradeUnitId()));
            accountInOut1.setAccountSubjectCode("2202");
            accountInOut1.setAccountSummary("销售发货快递费");
            accountInOut1.setAmount(logisticsAmount);
            accountInOut1.setAccountDirection(false);
            accountInOuts.add(accountInOut1);

            // 借：6601=销售费用
            AccountInOut accountInOut2 = buildDefaultAccountInOut(entity);
            accountInOut2.setAccountSubjectCode("6601");
            accountInOut2.setAmount(logisticsAmount);
            accountInOut2.setAccountSummary("销售发货快递费");
            accountInOut2.setAccountDirection(true);
            accountInOuts.add(accountInOut2);
        }

        // 销售收入记账
        BigDecimal incomeAmount = entity.getTotalAmount();
        if (entity.getTotalTaxAmount() != null && entity.getTotalTaxAmount().floatValue() != 0) {
            // 贷：222101=应交税费-应交增值税（销项税额）
            AccountInOut accountInOut3 = buildDefaultAccountInOut(entity);
            accountInOut3.setAccountSubjectCode("222101");
            accountInOut3.setAmount(entity.getTotalTaxAmount());
            accountInOut3.setAccountDirection(false);
            accountInOuts.add(accountInOut3);

            // 主营业务收入扣除应交税费
            incomeAmount = incomeAmount.subtract(entity.getTotalTaxAmount());
        }

        // 贷：6001=主营业务收入
        AccountInOut accountInOut4 = buildDefaultAccountInOut(entity);
        accountInOut4.setAccountSubjectCode("6001");
        accountInOut4.setAccountSummary("销售收入");
        accountInOut4.setAmount(incomeAmount);
        accountInOut4.setAccountDirection(false);
        accountInOuts.add(accountInOut4);

        // 已收账款
        BigDecimal payedAmount = entity.getPayedAmount();
        // 应收账款
        BigDecimal topayAmount = entity.getTotalAmount();
        if (payedAmount != null) {
            // 借：现金、银行、其他资金类型
            AccountInOut accountInOut5 = buildDefaultAccountInOut(entity);
            accountInOut5.setAccountSubject(entity.getAccountSubject());
            accountInOut5.setAccountSummary("销售单收款");
            accountInOut5.setAmount(payedAmount);
            accountInOut5.setAccountDirection(true);
            accountInOuts.add(accountInOut5);

            // 扣减已收账款
            topayAmount = topayAmount.subtract(payedAmount);
        }

        if (topayAmount.floatValue() != 0) {
            // 借：1122=应收账款
            AccountInOut accountInOut6 = buildDefaultAccountInOut(entity);
            accountInOut6.setAccountSubjectCode("1122");
            accountInOut6.setAccountSummary("销售应收账款");
            accountInOut6.setAmount(topayAmount);
            CustomerProfile customerProfile = entity.getCustomerProfile();
            accountInOut6.setBizTradeUnit(bizTradeUnitDao.findOne(customerProfile.getBizTradeUnitId()));
            accountInOut6.setAccountDirection(true);
            accountInOuts.add(accountInOut6);
        }

        accountInOutService.saveBalance(accountInOuts);

        // 结转成本记账
        List<AccountInOut> accountInOuts2 = Lists.newArrayList();
        // 借：6401=主营业务成本
        AccountInOut accountInOut7 = buildDefaultAccountInOut(entity);
        accountInOut7.setAccountSubjectCode("6401");
        accountInOut7.setAccountSummary("销售成本");
        accountInOut7.setAmount(entity.getCommodityAndTaxAmount());
        accountInOut7.setAccountDirection(true);
        accountInOuts2.add(accountInOut7);

        // 贷：1406=库存商品
        AccountInOut accountInOut8 = buildDefaultAccountInOut(entity);
        accountInOut8.setAccountSubjectCode("1406");
        accountInOut8.setAccountSummary("销售库存商品");
        accountInOut8.setAmount(entity.getCommodityAndTaxAmount());
        accountInOut8.setAccountDirection(false);
        accountInOuts2.add(accountInOut8);

        accountInOutService.saveBalance(accountInOuts2);

        saleDeliveryDao.save(entity);
        if (!entity.getVoucher().startsWith("R")) {
            BoxOrderDetail boxOrderDetail = entity.getBoxOrderDetail();
            if (boxOrderDetail != null && entity.getLogistics() != null) {
                Supplier logistics = supplierDao.findOne(entity.getLogistics().getId());
                boxOrderDetail.setLogisticsNo(entity.getLogisticsNo());
                boxOrderDetail.setLogisticsName(logistics.getAbbr());
                // 简化发货状态处理，一次完成发货
                boxOrderDetail.setOrderDetailStatus(BoxOrderDetailStatusEnum.S50DF);
                boxOrderDetail.setDeliveryFinishTime(new Date());
                boxOrderDetailDao.save(boxOrderDetail);
                BoxOrder boxOrder = boxOrderDetail.getBoxOrder();
                Boolean isOrderDeliveryFinish = true;
                for (BoxOrderDetail bod : boxOrder.getBoxOrderDetails()) {
                    if (!BoxOrderDetailStatusEnum.S50DF.equals(bod.getOrderDetailStatus())) {
                        isOrderDeliveryFinish = false;
                        break;
                    }
                }
                if (isOrderDeliveryFinish) {
                    boxOrder.setOrderStatus(BoxOrderStatusEnum.S50DF);
                    boxOrder.setDeliveryFinishTime(new Date());
                    boxOrderDao.save(boxOrder);
                }
                // 订单追踪表，发货的时候添加通知
                OrderTracing orderTracing = new OrderTracing();
                orderTracing.setBoxOrder(entity.getBoxOrderDetail().getBoxOrder());
                orderTracing.setOrderDetailSid(entity.getBoxOrderDetail().getId());
                orderTracing.setOccurTime(new Date());
                orderTracing.setTracingText("您的当期订单已发货");
                orderTracing.setCustomerProfile(entity.getCustomerProfile());
                orderTracingDao.save(orderTracing);
            }
        }

        // TODO:短信
        /*
         * Map<String, Object> dataMapSMS = new HashMap<String, Object>(); if
         * ("ALI".equals(boxOrder.getPayMode())) {
         * dataMapSMS.put("logisticsName", entity.getLogisticsName());
         * dataMapSMS.put("logisticsNo", entity.getLogisticsNo());
         * dataMapSMS.put("payMode", "ALI"); } if
         * ("OTHR".equals(boxOrder.getPayMode())) { dataMapSMS.put("payMode",
         * "OTHR"); } ConfigParameter configParameterSMS =
         * configParameterService
         * .findByCode(BizConfigParameterCodeEnum.IYB_ORDER_DELIVERY_NOTIFY_SMS
         * .name()); String smsHTML =
         * freemarkerConfigurer.processTemplate(configParameterSMS, dataMapSMS);
         * if (StringUtils.isNotBlank(smsHTML) &&
         * StringUtils.isNotBlank(entity.getMobilePhone())) {
         * 
         * 
         * sendMessage(entity.getMobilePhone(), smsHTML); }
         */
        // TODO:邮件
        // 发货通知邮件
        // businessNotifyService.sendDeliveryNotifyEmail(entity);
    }

    private AccountInOut buildDefaultAccountInOut(SaleDelivery entity) {
        AccountInOut accountInOut = new AccountInOut();
        accountInOut.setVoucher(entity.getVoucher());
        accountInOut.setVoucherType(VoucherTypeEnum.XS);
        accountInOut.setPostingDate(entity.getVoucherDate());
        return accountInOut;
    }

    private StockInOut buildDefaultStockInOut(SaleDelivery saleDelivery, SaleDeliveryDetail sdd) {
        CommodityStock commodityStock = findCommodityCommodityStock(sdd);
        StockInOut stockInOut = new StockInOut(saleDelivery.getVoucher(), sdd.getSubVoucher(), VoucherTypeEnum.XS, commodityStock);
        return stockInOut;
    }

    public void chargeAgainst(SaleDelivery entity) {
        Validation.isTrue(entity.getRedwordDate() == null, "销售单已经红冲");
        // Validation.isTrue(entity.getDeliveryTime() == null,
        // "已发货的不能红冲，只能走退货流程");
        // 移除工作流实例
        activitiService.deleteProcessInstanceByEntity(entity);
        entity.setRedwordDate(new Date());
        entity.setVoucherState(VoucherStateEnum.REDW);
        entity.setProcessType(DeliveryProcessTypeEnum.REDW);
        entity.setLastOperationSummary(entity.buildLastOperationSummary("红冲"));
        saleDeliveryDao.save(entity);
        // 克隆创建红冲对象，只复制主对象，明细行项数据不复制
        SaleDelivery redwordTarget = new SaleDelivery();
        String[] copyIgnoreProperties = ArrayUtils.addAll(entity.retriveCommonProperties(), new String[] { "saleDeliveryDetails", "voucher", "voucherDate", "saleDeliveryPics" });
        BeanUtils.copyProperties(entity, redwordTarget, copyIgnoreProperties);
        redwordTarget.setVoucherDate(new Date());
        redwordTarget.setVoucher("R" + entity.getVoucher());
        saleDeliveryDao.save(redwordTarget);

        // 处理库存红冲退回
        stockInOutService.redword(entity.getVoucher(), VoucherTypeEnum.XS, redwordTarget.getVoucher());
        // 处理关联的销售订单商品行项的库存红冲和锁定量
        for (SaleDeliveryDetail sdd : entity.getSaleDeliveryDetails()) {
            BoxOrderDetailCommodity bodc = sdd.getBoxOrderDetailCommodity();
            if (bodc != null && bodc.getStorageLocation() != null && bodc.getSalingLockedQuantity() != null) {
                CommodityStock commodityStock = commodityStockService.findBy(bodc.getCommodity(), bodc.getStorageLocation());
                StockInOut stockInOut = new StockInOut(bodc.getBoxOrderDetail().getVoucher(), bodc.getSubVoucher(), VoucherTypeEnum.XSD, commodityStock);
                stockInOut.setDiffSalingQuantity(sdd.getSalingLockedQuantity());
                stockInOut.setOperationSummary("红冲 重新锁定 销售订单商品行项 在销售单中的使用量");
                stockInOutService.saveCascade(stockInOut);
                bodc.setSalingLockedQuantity(bodc.getSalingLockedQuantity().add(sdd.getSalingLockedQuantity()));
                boxOrderDetailCommodityDao.save(bodc);
            }

        }
        if (entity.getDeliveryTime() != null) {
            BoxOrderDetail boxOrderDetail = entity.getBoxOrderDetail();
            if (boxOrderDetail != null) {
                boxOrderDetail.setLogisticsNo(null);
                boxOrderDetail.setLogisticsName(null);
                boxOrderDetail.setDeliveryFinishTime(null);
                boxOrderDetailDao.save(boxOrderDetail);
            }
        }

        // 处理财务红冲退回
        accountInOutService.redword(entity.getVoucher(), VoucherTypeEnum.XS, redwordTarget.getVoucher());
    }

    /**
     * 供工作流在所有审批完成后的回调接口
     */
    public void bpmAuditPost(SaleDelivery entity) {
        entity.setAuditDate(new Date());
        entity.setAgentAssignDate(entity.getAuditDate());
        entity.setLastOperationSummary(entity.buildLastOperationSummary("审核完结"));
        entity.setVoucherState(VoucherStateEnum.POST);
        entity.setProcessType(DeliveryProcessTypeEnum.PASS);
        SaleReturn saleReturn = null;
        if (entity.getSaleReturn() != null) {
            saleReturn = saleReturnDao.findOne(entity.getSaleReturn().getId());
        }
        if (entity.getBoxOrderDetail() != null) {
            BoxOrderDetail boxOrderDetail = boxOrderDetailDao.findOne(entity.getBoxOrderDetail().getId());
            if (BoxOrderDetailStatusEnum.S10O.equals(boxOrderDetail.getOrderDetailStatus()) || BoxOrderDetailStatusEnum.S15O.equals(boxOrderDetail.getOrderDetailStatus())
                    || BoxOrderDetailStatusEnum.S20PYD.equals(boxOrderDetail.getOrderDetailStatus())
                    || BoxOrderDetailStatusEnum.S25PYD.equals(boxOrderDetail.getOrderDetailStatus())) {
                boxOrderDetail.setOrderDetailStatus(BoxOrderDetailStatusEnum.S35C);
                boxOrderDetailDao.save(boxOrderDetail);
            }
        }
        if (saleReturn == null || !SaleReturnTypeEnum.REP.equals(saleReturn.getSaleReturnType())) {
            for (SaleDeliveryDetail sdd : entity.getSaleDeliveryDetails()) {

                BoxOrderDetailCommodity bodc = sdd.getBoxOrderDetailCommodity();

                if (bodc != null && bodc.getSalingLockedQuantity() != null && bodc.getStorageLocation() != null) {
                    if (sdd.getQuantity().compareTo(bodc.getSalingLockedQuantity()) >= 0) {
                        sdd.setSalingLockedQuantity(bodc.getSalingLockedQuantity());
                    } else {
                        sdd.setSalingLockedQuantity(sdd.getQuantity());
                    }
                    CommodityStock commodityStock = commodityStockService.findBy(bodc.getCommodity(), bodc.getStorageLocation());
                    Validation.isTrue(commodityStock != null, "销售订单商品行项，商品：(" + bodc.getCommodity().getSku() + ")和锁定库存地：(" + bodc.getStorageLocation().getTitle() + ")无法找到对应的库存记录");
                    StockInOut stockInOut = new StockInOut(bodc.getBoxOrderDetail().getVoucher(), bodc.getSubVoucher(), VoucherTypeEnum.XSD, commodityStock);
                    stockInOut.setDiffSalingQuantity(sdd.getSalingLockedQuantity().negate());
                    stockInOut.setOperationSummary("退回 关联销售订单行项锁定库存量 在此销售单行的使用量");
                    stockInOutService.saveCascade(stockInOut);
                    bodc.setSalingLockedQuantity(bodc.getSalingLockedQuantity().subtract(sdd.getSalingLockedQuantity()));
                    // 把库存商品成本价带入boxOrderDetailCommodity的costPrice 字段中
                    bodc.setCostPrice(sdd.getCostPrice());
                    bodc.setCommodityAmount(sdd.getCostAmount());
                    boxOrderDetailCommodityDao.save(bodc);

                }
                // 如果不是“代发”的销售单，才进行库存量的处理
                if (!StorageModeEnum.SA.equals(sdd.getStorageLocation().getStorageMode())) {
                    StockInOut stockInOut = buildDefaultStockInOut(entity, sdd);
                    stockInOut.setDiffSalingQuantity(sdd.getQuantity());
                    stockInOut.setOperationSummary("提交销售单锁定库存量");
                    stockInOutService.saveCascade(stockInOut);
                }

                // 更新商品最近销售价格
                if (!Boolean.TRUE.equals(sdd.getGift())) {
                    // 新增行项时商品对象为Web层构建的空数据对象，需要DAO基于主键查询加载处理
                    Commodity commodity = commodityDao.findOne(sdd.getCommodity().getId());
                    CommodityVaryPrice commodityVaryPrice = commodity.getCommodityVaryPrice();
                    commodityVaryPrice.setLastSalePrice(sdd.getPrice());
                    commodityVaryPriceDao.save(commodityVaryPrice);
                }
            }
        }
        saleDeliveryDao.save(entity);
        if (StorageModeEnum.AUTO.equals(entity.getStorageMode())) {
            this.pickSave(entity);
        }
    }

    /**
     * 供销售单关联的代发模式的采购订单，录入发货后调用
     */
    public void deliveryByPurchase(PurchaseOrder purchaseOrder) {
        Assert.isTrue(purchaseOrder.getSaleDelivery() != null && StorageModeEnum.SA.equals(purchaseOrder.getStorageMode()));

        SaleDelivery saleDelivery = purchaseOrder.getSaleDelivery();
        // 销售单发货
        saleDelivery.setLogistics(purchaseOrder.getLogistics());
        saleDelivery.setLogisticsNo(purchaseOrder.getLogisticsNo());
        saleDelivery.setLogisticsAmount(purchaseOrder.getTotalDeliveryAmount());

        for (PurchaseOrderDetail purchaseOrderDetail : purchaseOrder.getPurchaseOrderDetails()) {
            purchaseOrderDetail.setRecvQuantity(purchaseOrderDetail.getQuantity());
            Assert.isTrue(purchaseOrderDetail.getSaleDeliveryDetail() != null);
            SaleDeliveryDetail saleDeliveryDetail = purchaseOrderDetail.getSaleDeliveryDetail();
            Assert.isTrue(saleDeliveryDetail != null);
            saleDeliveryDetail.setCostPrice(purchaseOrderDetail.getCostPrice());
            saleDeliveryDetail.setCostAmount(purchaseOrderDetail.getCostAmount());
            saleDeliveryDetailDao.save(saleDeliveryDetail);
        }
        purchaseOrderDao.save(purchaseOrder);
        BigDecimal commodityCostAmount = BigDecimal.ZERO;
        for (SaleDeliveryDetail sdd : saleDelivery.getSaleDeliveryDetails()) {
            commodityCostAmount = commodityCostAmount.add(sdd.getCostAmount());

        }
        saleDelivery.setCommodityCostAmount(commodityCostAmount);
        saleDelivery.setTotalCostAmount(commodityCostAmount.add(saleDelivery.getLogisticsAmount()));
        this.deliverySave(saleDelivery);

    }

    public SaleDelivery doSave(SaleDelivery entity) {
        return saleDeliveryDao.save(entity);
    }

    public void deliveryNotify(String preEmailContent, String preEmail, String preSub, String preSmsContent, String preMobile) {
        if (StringUtils.isNotBlank(preMobile) && StringUtils.isNotBlank(preSmsContent)) {
            businessNotifyService.sendMsgAsync(preMobile, preSmsContent);
        }
        if (StringUtils.isNotBlank(preEmail) && StringUtils.isNotBlank(preEmail)) {
            mailService.sendHtmlMail(preSub, preEmailContent, false, preEmail);
        }
    }

    public List<SaleAndReturnDetailVo> findSaleAndReturnLists(String kw, Date begin, Date end, String[] voucherTypes) {
        MapSqlParameterSource args = new MapSqlParameterSource();

        // 日期搜索
        args.addValue("begin", begin == null ? "" : begin);
        args.addValue("end", end == null ? "" : end);
        // 关键字搜索
        args.addValue("kw", "%" + (kw == null ? "" : kw) + "%");

        StringBuilder sql = new StringBuilder();
        sql.append("SELECT * FROM ( ");
        sql.append("SELECT ");
        sql.append("sd.voucher,");// 凭证号
        sql.append("'XS' as voucher_type,");
        sql.append("sdd.sid,");// sid
        sql.append("sd.submit_date as created_dt,");// 销售日期
        sql.append("c.barcode + c.title as title,");// 商品名
        sql.append("sdd.quantity,");// 数量
        sql.append("sdd.price,");// 单价
        sql.append("sdd.original_amount,");// 原价
        sql.append("sdd.discount_amount,");// 折扣额
        sql.append("sdd.amount,");// 折后金额
        sql.append("sdd.commodity_and_tax_amount,");// 含税总金额
        sql.append("sdd.cost_price,");// 含税进价
        sql.append("sd.storage_mode");// 库存模式
        sql.append(" FROM ");
        sql.append("myt_sale_delivery_detail sdd,");
        sql.append("iyb_commodity c,");
        sql.append("myt_sale_delivery sd");
        sql.append(" WHERE ");
        sql.append("c.sid = sdd.commodity_sid");
        sql.append(" AND ");
        sql.append("sdd.order_sid = sd.sid");
        sql.append(" and sd.voucher_state='POST'");
        sql.append(" UNION ");

        sql.append(" SELECT ");
        sql.append("sr.voucher,");// 凭证号
        sql.append("'TH' as voucher_type,");
        sql.append("srd.sid,");// sid
        sql.append("srd.created_dt,");// 销售日期
        sql.append("c.barcode + c.title as title,");// 商品名
        sql.append("srd.quantity * -1 as quantity,");// 数量
        sql.append("srd.price,");// 单价
        sql.append("srd.original_amount * -1 as original_amount,");// 原价
        sql.append("srd.discount_amount,");// 折扣额
        sql.append("srd.amount * -1 as amount,");// 折后金额
        sql.append("srd.amount * -1 as amount,");// 含税总金额
        sql.append("srd.commodity_price,");// 含税进价
        sql.append("ssl.storage_mode ");// 库存模式
        sql.append(" FROM ");
        sql.append("myt_sale_return_detail srd,");
        sql.append("iyb_commodity c,");
        sql.append("iyb_stock_storage_location ssl,");
        sql.append("myt_sale_return sr");
        sql.append(" WHERE ");
        sql.append("c.sid = srd.commodity_sid");
        sql.append(" AND ");
        sql.append("ssl.sid = srd.storage_location_sid ");
        sql.append(" and sr.voucher_state='POST'");
        sql.append(" AND ");
        sql.append("sr.sid = srd.sale_return_sid");
        sql.append(") item");
        sql.append(" WHERE (item.created_dt >= :begin AND item.created_dt <= :end)");// 关键字搜索
        sql.append(" AND (item.voucher LIKE :kw OR item.title LIKE :kw)");// 日期搜索
        String voucherType = StringUtils.join(voucherTypes, "','");
        sql.append(" AND (item.voucher_type in ('" + voucherType + "'))");
        sql.append(" ORDER BY item.created_dt DESC");

        NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);

        final List<SaleAndReturnDetailVo> saleAndReturnDetailVos = Lists.newArrayList();
        namedParameterJdbcTemplate.query(sql.toString(), args, new RowCallbackHandler() {

            @Override
            public void processRow(ResultSet rs) throws SQLException {
                SaleAndReturnDetailVo vo = new SaleAndReturnDetailVo();
                vo.setSid(rs.getLong("sid"));
                vo.setVoucher(rs.getString("voucher"));
                vo.setCreatedDate(rs.getDate("created_dt"));
                vo.setCommodityTitle(rs.getString("title"));
                vo.setQuantity(rs.getBigDecimal("quantity"));
                vo.setPrice(rs.getBigDecimal("price"));
                vo.setOriginalAmount(rs.getBigDecimal("original_amount"));
                vo.setVoucherType(rs.getString("voucher_type"));
                vo.setDiscountAmount(rs.getBigDecimal("discount_amount"));
                vo.setAmount(rs.getBigDecimal("amount"));
                vo.setCommodityAndTaxAmount(rs.getBigDecimal("commodity_and_tax_amount"));
                vo.setCostPrice(rs.getBigDecimal("cost_price"));
                String sm = rs.getString("storage_mode");
                if (StringUtils.isBlank(sm)) {
                    vo.setStorageMode(null);
                } else {
                    vo.setStorageMode(StorageModeEnum.valueOf(StorageModeEnum.class, sm));
                }
                saleAndReturnDetailVos.add(vo);
            }
        });
        return saleAndReturnDetailVos;
    }
}
